/**
 * @author wn
 * @date 2022/12/07 14:31:11
 * @description: TS 学习笔记
 */

/*
安装 TypeScrip 编译器

npm i typescript -g

运行 TS 文件

tsc  hello.ts     function test(){}

-->  hello.js     function test(){}

node hello.js

问题 1 编译后 函数名相同  报错函数名相同
     tsc --init
    生成  tsconfig.json    配置文件  Strict 关闭严格模式
    2 编译TS 后 还要再次 运行  tsc  hello.ts
    tsc --watch
    3 TS 报错  依旧 编译 为 js
    tsc -noEmitOnError --watch

		any 可以是任何值,有任何函数调用
    let d: any = {}
    d = 1
		d.do()

    unknown 可以是任何值 没有函数调用

		never 可以给任何值赋值  任何值都不可以赋值 never

		interface A { ():void } 一个函数

		interface Animal {
	    live(): void // 一个对象 里面有 live 函数
	    live2: () => void
    }

		{ [i: number]: string }-->数组  keyof number
		{ [i: string]: string } -->对象  key 为 number|string

		数组索引 number -- T[number] 对象 number|string  具体对象{ name:string } 索引 T["name"]

*/

// 显式类型  类似 Kotlin val name : String   val b = "b"
const test = (person: String) => {
	console.log(person)
}
test('1')
// 类型推断
let a = 'a'
a = '1'
// 降级编译  向下兼容浏览器  编译成 更被认识的 代码
// tsconfig.json -- target:"es5"    es3

// 严格模式
/*
  strict:true    等同于下面俩
  noImplicitAnt:true       推断为 Any
  strictNullChecks:true    undefined null
*/
// 常用类型   string number boolean
// 数组
const b: number[] = [10, 11]
const c: Array<number> = [10, 11]

// any  类型检查几乎都是合法的
let d: any = {}
d = 1

// 函数返回值  无返回值  void  同 Java
const test2 = () => {
	console.log(1)
}
// 上下文 可以推断 函数参数类型
const e = [1, 2, 3]
e.forEach(v => console.log(v))

// 对象类型
const f = { x: '1', y: 2 }

// ? 可以不传参数   不传 为 undefined  需要 判空
const test3 = (obj: { name?: string }) => {
	console.log(obj.name?.toUpperCase)
}
test3({})
test3({ name: '? -- 可以传 也可以不传 ' })

// 联合类型  |  typeof g === "string"
// 使用时  需要判断  该类型 是否有 后续操作
const g: string | number = 1

// type  定义 类型  别名
type myString = string
type obj = { name: string; age: number }
type two = string | number

// 接口  定义  对象类型
interface MyObj {
	name: string
	age: number
}
const h: MyObj = { name: '1', age: 1 }

// 扩展接口 extends  type &
interface Animal {
	name: string
}
interface Bear extends Animal {
	honey: boolean
}
// const bear: Bear = {
// 	name: 'ww',
// 	honey: true,
// }
type Animal2 = { name: string }
type Bear2 = Animal2 & { honey: boolean }
const bear2: Bear2 = {
	name: 'ww',
	honey: true,
}
// 添加字段  interface 重复定义同名即可  type 无法重名 只能 &
interface Animal3 {
	age: number
}
interface Animal3 {
	hobby: number[]
}

// 类型 断言    as   <>   针对 值
const i = document.getElementById('div') as HTMLDivElement
const j = <HTMLDivElement>document.getElementById('div')
// 类型需要有覆盖关系   否则 先断言 为 unknown 或者 any  再 断言为 指定类型
const k = 'string' as unknown as number

// 类型约束
const l: true = true
const m: 'hello' = 'hello'
const request = (url: string, method: 'GET' | 'POST') => {}
// req.method 只能推断为 string  无法推断为 POST
const req = { url: 'www.baidu.com', method: 'POST' as 'POST' }
const req2 = { url: 'www.baidu.com', method: 'POST' } as const

request(req.url, req.method)
request(req2.url, req2.method)

// 断言  !   x可能不存在 或者 null   断言为 number 类型
const danger = (x?: number | null) => {
	console.log(x!.toFixed())
}

// 枚举 enum
enum Direction {
	UP = 1,
	DOWN,
	LEFT,
	RIGHT,
}
console.log(Direction.UP) // 1  其他递增 2 3 4

// bigint Symbol
// 大的number     唯一性的指向    es2020 才有的类型
const oneHundred: bigint = BigInt(100)
// const anotherHundred: bigint = 100n

// name2 !== name3
const name2 = Symbol('ww')
const name3 = Symbol('ww')

// in 操作符缩小
type Fish = { swing: () => void }
type Bird = { fly: () => void }
function move(animal: Fish | Bird) {
	if ('swing' in animal) animal.swing()
	else animal.fly()
}

// instanceof  是否是 实例
function logValue(value: Date | string) {
	value instanceof Date ? value.toUTCString() : value.toUpperCase
}
logValue(new Date())

// 类型 谓词   必须 -- 参数名 is type
// true Fish   false Bird
function isFish(pet: Fish | Bird): pet is Fish {
	return (pet as Fish).swing !== undefined
}
const isFish2 = (pet: Fish | Bird): pet is Fish => {
	return (pet as Fish).swing !== undefined
}

// unions  联合类型
type Shape2 = Fish | Bird

// never 类型 不存在的状态 never 可以给任何类型  (任何类型都不可以 给 never)
let n: never
const o: string = n // never 可以给任何类型
// n = 1   任何类型都不可以 给 never

// 函数表达式
type fn = (a: string) => void
const fn1 = (block: fn) => block('传入String')
fn1((s: string) => {
	console.log(s.toUpperCase)
})

// 调用签名   给函数 添加属性
// ds  为一个 函数  包含 一个属性
// 设计成对象  函数部分 : 替代 =>
type ds = {
	description: string
	(params: number): boolean
}
const fn2 = (block: ds) => {
	block(6)
	const d = block.description
}
const block = (params: number) => true
block.description = '111'
fn2(block)

// 构造签名  new () : type
interface CallOrConstructor {
	// 调用签名
	(n: number): Date
	// 构造签名
	new (s: string): Date
}
const date = (d: CallOrConstructor) => {
	d(2022) // 调用      函数
	new d('2022') // 需要实例化 函数
}

// 泛型  T
const firstElement = <T>(arr: T[]): T => arr[0]
function firstElement2<T>(arr: T[]): T {
	return arr[0]
}
firstElement([1, 2, 'a'])
firstElement<number | string>([1, 2, 'a'])

// 泛型  继承  extends
const longest = <T extends { length: number }>(a: T, b: T) =>
	a.length > b.length ? a : b
longest({ length: 1 }, { length: 2 })
longest([1, 2], [2, 3])
longest('[], string', '有length 属性')

// 指定类型
const combine = <T>(a: T[], b: T[]): T[] => a.concat(b)
combine<string | number>([1, 2], ['1'])

// 3 个准则
// ? 可选参数  与 = 默认参数  二选一
function choose(a?: string) {}
function choose2(a: (p?: string) => void) {
	a('aaa')
	a()
}

// 高阶函数  参数  不要为空   ?
choose2(p => p?.toLocaleUpperCase)

// 函数重载
function makeDate(t: string): Date // 重载 签名
function makeDate(m: number, y: number, d: number): Date // 重载 签名
// 实现 签名
function makeDate(o: string | number, y?: number, d?: number): Date {
	return y !== undefined && d !== undefined ? new Date(y, y, d) : new Date(1)
}
// 实现签名   参数 必须 结合 所有 重载签名 定制--按照之前重载 的顺序 类型  叠加 定制  1 1 对应 1 -- 实现1 包含俩重载1的联合类型
// 调用时  只能 按照 重载签名的方式 参数类型  调用

/*

this 作为参数  可以传入 实例
Ts 无返回值   默认返回  void   js--undefined
TS object (! string number bigint boolean null undefined symbol)
TS object 是 类型 -- a: object 不同于 js { } Object
unknown 代表 任何值  但是 不管做什么都不合法  未知的不能做任何事
any     代表 任何值  可以有任何方法

*/

function f1(a: any) {
	a.b()
}
function f2(a: unknown) {
	// a.b() 未知的不能做任何事
}
// never 不会被观察到   返回 异常  死循环等
// Function 调用  返回 any  最好使用 ()=>void

// 参数展开  ...m:number[] -- 1,2,3,4   kotlin vararg    *[]  fun multiply(n:Int,vararg m : Int)  multiply(1,*[2,3,4,5,6])
// ... 展开运算
function multiply(n: number, ...m: number[]) {}
multiply(1, 2, 3, 4, 5, 6)
// 实参展开
multiply(1, ...[2, 3, 4, 5, 6])

// 数组的个数 是 0 或者多个  不固定  as const 变成常量  就固定了
const args = [1, 2] as const
const angle = Math.atan2(...args) // 参数 为 2个     坐标值 求 弧度

// 参数解构
type ABC = { a: number; b: number }
function sum({ a, b }: ABC) {
	a + b
}
sum({ a: 1, b: 2 })

// 直接定义函数 返回值  void  无法返回
function fun(): void {
	/* return true  */
}
const fun2 = function (): void {
	/* return true */
}
// 定义函数类型  就可以  -- Function
type funV = () => void
const fun3: funV = () => true
const fun4: funV = function () {
	return true
}
const f5: void = fun3()

// 对象 属性   可选
interface Shape {
	x?: number
	y?: number
}
function paintShape({ x = 0, y = 0 }: Shape) {
	x + y
}

// 只读属性 readonly
// readonly 修饰 不可以更改
// readonly 修饰 复杂数据 {} 对象不可更改  对象内属性 可以更改
interface Home {
	readonly name: string
	readonly person: {
		age: number // 可以更改
	}
}
// 类型兼容
interface Person {
	age: number
}
interface ReadPerson {
	readonly age: number
}
const p: Person = { age: 18 }
const p2: ReadPerson = p
p.age++
// p2.age++

// 索引签名 格式 [x:xx]:xx
// 1 number 用来约束数组
interface arrayString {
	[i: number]: string
}
const as: arrayString = ['a', 'b']
// 2 string 用于约束对象
interface stringType {
	[i: string]: string
	x: string
	// y: number 必须 string
}
const as3 = { name: 'name', x: 'x', y: 'y' }

// 扩展类型
interface Colorful {
	color: string
}
interface Circle {
	radius: number
}
interface cAndC extends Colorful, Circle {}

// 交叉类型
type cAndC2 = Colorful & Circle & { hobby: string }
const cAndC3: Colorful & Circle = { color: 'red', radius: 100 } // 匿名方式
const cAndC4: cAndC2 = { color: 'red', radius: 100, hobby: '吃饭' }
// interface--extends    type--&
// interface 可以同名 叠加

// 泛型 对象
interface Box<T> {
	content: T
}
const box: Box<string> = {
	content: 'cc',
}

type Box2<T> = { content: T }
const box2: Box2<string> = {
	content: 'cc',
}

// 调用签名 构造签名
// interface { (arg: T): T } 就是一个函数
// 泛型 类型
interface Identity<T> {
	(arg: T): T // 函数
}
// 泛型加载接口上  需要指定类型 更严格
const identity: Identity<number> = <T>(arg: T): T => arg

identity(1)

interface Identity2 {
	<T>(arg: T): T // 函数中定义泛型
}
const identity2: Identity2 = <T>(arg: T): T => {
	return arg
}
identity2('1')

// 泛型 类
// 属性需要初始化 严格校验
class PersonNumber<T> {
	age: T
	add: (arg: T) => T
}
const pn = new PersonNumber<number>()
pn.age = 1
pn.add = arg => {
	return arg
}
pn.add(1)

// 泛型约束 extends
interface Lengthwise {
	length: number
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
	// arg 有length 属性  可以设置泛型约束
	arg.length
	return arg
}
loggingIdentity('ww')
loggingIdentity([1, 2])
loggingIdentity({ length: 1 })

// 泛型约束 中 使用 类型参数  keyof
// 一个泛型  属于 另一个泛型的 key
function getProperty<A, B extends keyof A>(obj: A, key: B) {
	return obj[key]
}
getProperty({ name: 'wn', age: 1 }, 'age')

// 泛型中  使用  类 c: { new () : T } -- c: new () => T)     { ():T } = ()=>T
// 返回 类的实例
function create<T>(c: { new (): T }): T {
	return new c()
}
create(PersonNumber)

function create2<T>(c: new () => T): T {
	return new c()
}
create2(PersonNumber)
// class PersonNumber ==  new () => T    new 构造函数

// keyof 可以获得联合类型
type Point = { x: number; y: number }
type p = keyof Point // x | y
const p1: p = 'x'
const p3: p = 'y'

type ArrayIs = { [i: number]: string } // { [i: number]: string }-->数组
type A = keyof ArrayIs // number
const a1: A = 1

type MapIs = { [i: string]: string } // { [i: string]: string } -->对象  key 为 number|string
type M = keyof MapIs // number|string
const m1: M = 1
const m2: M = 'name'

const obj = {
	1: 'number可以为 key',
	name: '1',
}

// typeof 定义类型    js 获取类型的值 -- typeof "ss" === string
const s = 'hello'
let N: typeof s
N = 'hello'

// typeof 后面只能跟 变量 或者函数  而不是函数的调用
const msgBox = () => {}
const shouldContinue: typeof msgBox = () => {}

// ReturnType<T> T 必须为一个函数类型  ReturnType返回 该函数的返回值 类型
type Predicate = (arg: unknown) => boolean
type K = ReturnType<Predicate>
const myK: K = true

const F = () => {
	return { a: 1 }
}
type FF = ReturnType<typeof F>
const ff: FF = { a: 2 }

// 索引访问类型   key 是 type 不是值  typeof "age" = age 类型
interface MyPerson {
	name: string
	age: number
}
type I1 = MyPerson['name'] // string
type I2 = MyPerson[keyof MyPerson] // string | number

const key = 'name'
type I3 = MyPerson[typeof key] // string

// 条件类型   live函数
interface Animal0 {
	(): void // 一个函数
}
interface Animal {
	live(): void // 一个对象 里面有 live 函数
	live2: () => void
}
const AAAA: Animal0 = () => {}
const AAAA2: Animal = {
	live() {},
	live2() {},
	name: '',
}

type NameOrString<T extends number | string> = T extends number
	? number
	: string

const createN = <T extends number | string>(arg: T): NameOrString<T> => {
	throw ''
}
createN<number>(1)
const NameIs: NameOrString<number> = 1

// 条件类型约束   数组索引 number  对象 number|string  { name:string } 索引 "name"
type Flatten<T> = T extends any[] ? T[number] : T
type str = Flatten<string[]> // 返回数组成员 类型
type num = Flatten<number> // 返回 T 类型

// infer 后自定义类型
type GetReturnType<T> = T extends (...arg: any[]) => infer R ? R : never
type NumType = GetReturnType<(x: number, y: string) => number>
const numType: NumType = 1

// 分布式
type ToArray<T> = T extends any ? T[] : never
type SN = ToArray<string | number> // string[] | number[]

type ToArray2<T> = [T] extends [any] ? T[] : never
type SN2 = ToArray2<string | number> // (string| number)[]

// 类
class PointClass {
	name!: string // 断言 可以不赋值
	age = 18 // 同 constructor 中 赋值
	x: number
	readonly y: number = 0 // 在构造函数中赋值(代码块中有值  可以在 构造中二次赋值) 初始化无法更改
	constructor() {
		this.x = 1
		this.y = 1 // 构造中二次赋值
	}
	calc() {
		this.x + this.y
	}
	// get set
	// 只有get  没有set  无法 pc._x = 100
	// set 参数没有类型  可以 从 get 中推断
	get _x() {
		return this.x
	}
	set _x(value) {
		this.x = value
	}
}
const pc = new PointClass()
pc._x // 直接 名字即可
pc._x = 100

class Thing {
	_size = 0
	get size() {
		return this._size
	}
	set size(value: number | string | boolean) {
		const num = Number(value)
		if (!Number.isFinite(num)) {
			// isFinite 是数字
			this._size = 0
			return
		}
		this._size = num
	}
}
const thing = new Thing()
thing.size = 'hh'

// class 索引签名   理解为 class 的索引   索引规定  属性 与 方法
// check 为函数类型 走后面  this[] 为数组类型 走前面
class MyClass {
	[s: string]: boolean | ((s: string) => boolean)

	x = true
	check(s: string) {
		return this[s] as boolean
	}
	check2() {
		return true
	}
}

// implements   实现接口  必须实现接口中 属性 和 方法
interface NameCheck {
	x?: number
	y: number
	check(arg: string): boolean
}
// 接口中可选属性  class 可以不实现 实例无法访问
// 接口中 方法参数  在 class  中 为 any  但是起码要联合类型 含有 接口方法参数类型
class NameCheckClass implements NameCheck {
	y = 1
	check(s: number | string) {
		return true
	}
}

// extends   继承

// class  重写 方法  子类重写 父类 方法  方法名相同  参数要 兼容

/*
基类的成员初始化
基类的构造函数初始化
子类的成员初始化
子类的构造函数初始化
*/
class Base {
	name = 'base'
	constructor() {
		console.log('My name is ' + this.name)
	}
}

class Derived extends Base {
	name = 'derived'
	constructor() {
		super()
		console.log('My name is ' + this.name) // 加了 constructor 先 base  后 derived
	}
}

// Prints "base", not "derived"   子类没有构造函数
const der = new Derived()

// 内置 class 内置类型
// ES5 版本较低 有问题  需要手动设置 原型
class MsgError extends Error {
	constructor(m: string) {
		super(m)
		// 明确 设置 原型  __proto__ -- prototype
		Object.setPrototypeOf(this, MsgError.prototype) // 不设置  isInstance=false 设置 true
	}
}
const msgError = new MsgError('hello')
const isInstance = msgError instanceof MsgError

/**
 * 成员可见性 -- 限制在 类中  { 访问 }
 * public      默认  任何地方都可访问
 * protected   当前类 和 子类 继承 this 访问    当前 类实例  子类 实例 不可
 * private     私有的 当前类中可以访问          当前 类实例  不可
 */
class AA {
	protected x = 10
	private y = 10
	z = 10
	test(a: AA) {
		return this.y === a.y // 只在 类中 可以访问
	}
}

class BB extends AA {
	test2() {
		console.log(this.x) // 子类 可以访问 protected
	}
}
const bb = new BB()
// bb.x  子类实例 无法访问 protected
const aa = new AA()
// aa.x
// aa.y
aa.z

// 静态成员  static    类直接调用
// 可以被继承  子类直接调用

// static 区块  #--私有 只在 类内部可以访问 感觉同 private

/* class Foo {
	static #count = 1
	test() {
		Foo.#count = 2
	}
	static {
		Foo.#count += 3
	}
} */

// Foo.#count

// 泛型 class        静态属性 不可以 引用 class  泛型
class Box<T> {
	content: T
	constructor(v: T) {
		this.content = v
	}
	// static b: T   静态成员 不可以 引用 class  泛型
}
const box1 = new Box(1)

// 调用者 getName 需要是 this   相当于 调用者 隐式绑定this (结构与 My 一致 或 多于)
class My {
	name = 'My'
	// getName0 = () => this.name // My
	getName(this: My) {
		return this.name
	}
}
const my = new My()
my.getName() // 调用绑定 this   kotlin 扩展函数  隐式绑定 T T.block()  block(T) block.invoke(T)

const obj1 = {
	name: 'obj',
	getName: my.getName,
	age: 18,
}
const myName = obj1.getName() // obj  对象有this (结构与 class >=)  指向 obj
const myName2 = my.getName
// myName2() 调用时 没有绑定 this

// this 类型
class Box1 {
	a: 1
	same(other: this) {}
	private c = 2 // this  为实例  本身的实例也不可访问 private 子类实例也不可访问
}
class Derived1 extends Box1 {
	b: 1
}
const boxBase = new Box1()
const derived1 = new Derived1()
// derived1.same(boxBase)  this 指 子类  父类不能赋值给子类  因为 父类中没有子类独有 属性 与 方法
boxBase.same(derived1) // 子类继承父类所有 属性 与 方法  子类可以赋值 给 父类   out -- 协变

// 基于 类型守卫 的 function() : this is T { return boolean }
/**
 * instanceof    class
 * typeof        type
 * in            {}
 *
 */

// 参数属性  public protected private readOnly 修饰 constructor 相当与 类 成员  -- kotlin 主 构造函数
class Params {
	age = 10
	constructor(public name: string, age: number) {
		this.age = age
	}
}
const pa = new Params('ww', 18)
pa.name
pa.age

// 类 表达式
const Foo = class {}
const foo = new Foo()

// 抽象类 abstract  作为 基类  内部也需要 abstract 修饰 方法没有方法体 属性没有值
// 无法实例化  可以被 继承
abstract class ABClass {
	abstract name: string
	abstract getName(): string
	printName() {} //可以有普通方法
}

class SunClass extends ABClass {
	name = ''
	getName(): string {
		return ''
	}
}

// 抽象类 做 构造器 new () => ABClass   { new (): ABClass }
function greet(cons: new () => ABClass) {
	const instance = new cons()
}
greet(SunClass) //也必须是 子类 子类才可以实例化

// 类的关系   结构相同  就可以 const p : P1 = new P2()  P2 >= P1 即可
class Empty {}
function fn(arg: Empty) {}
fn({})
fn(window)
fn(null)
fn(100)

// TS 模块

/**
 * import default, { pi } from ""
 *
 * import * as math from ""
 * math.pi
 * math.default
 */

export type cat1 = { name: string }
import { type cat, createName } from '@/note/Test'
const cc: cat = { name: '喵喵' }
createName()

// CommonJS
module.exports = {
	pi: 3.14,
	fn: () => '',
}
exports.fn = () => ''

const maths = require('@/note/Test')
maths.pi

/**
 * 声明文件  声明引入的文件 后面直接用即可
 * 一般放在 .d.ts  文件中
 * 无需每一个使用都自己声明  一般 库都提供插件 安装后使用即可 -- TypeSearch 搜索即可
 *  有的库 自带 无需再次安装 声明
 */
declare const jQuery: () => void
jQuery()

// 自带内置类型
